package net.wangds.yunz.auth.filter;


import io.jsonwebtoken.Claims;
import net.wangds.auth.exception.*;
import net.wangds.auth.token.cache.TokenCache;
import net.wangds.jwt.comp.JwtTokenComp;
import net.wangds.log.helper.LogHelper;
import net.wangds.program.auth.module.AuthAccount;
//import net.wangds.program.auth.service.M10AuthService;
import net.wangds.spring.utils.SpringUtils;
import net.wangds.utils.IpAddressUtils;
import net.wangds.utils.JsonUtils;
import net.wangds.web.entrypoint.BaseResp;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.stereotype.Service;
import org.springframework.util.MimeTypeUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;

/**
 * Jwt认证Filter.
 */
@Service
public class JwtAuthenticationTokenFilter extends BasicAuthenticationFilter {

    @Autowired
    private JwtTokenComp mmCmpJwtToken;

    //@Autowired
    //private M10AuthService mmServAuth;

    private AuthenticationEntryPoint onAuthException;

    public JwtAuthenticationTokenFilter(AuthenticationManager authenticationManager,
                                        AuthenticationEntryPoint onAuthenticationException
    ) {
        super(authenticationManager, onAuthenticationException);
        this.onAuthException = onAuthenticationException;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        //LogHelper.dev(()->"执行Jwt认证过滤器");
        try {
            String accessToken = request.getHeader(HttpHeaders.AUTHORIZATION);
            SecurityContextHolder.getContext().setAuthentication(getAuthentication(accessToken, request));
            chain.doFilter(request, response);
        }catch(AuthenticationException e){
            //处理认证异常
            this.onAuthException(request, response, e);
        }  catch (Exception e) {
            //处理其他异常
            this.onServerException(request, response, e);
        }
    }

    protected void onAuthException(HttpServletRequest request, HttpServletResponse response,AuthenticationException e){
        this.writeException(HttpStatus.UNAUTHORIZED, request, response, JsonUtils.toJsonStr(BaseResp.createDefault().code(-1).msg(e.getLocalizedMessage())));
    }

    protected void onServerException(HttpServletRequest request, HttpServletResponse response,Exception e){
        this.writeException(HttpStatus.INTERNAL_SERVER_ERROR, request, response, JsonUtils.toJsonStr(BaseResp.createDefault().code(-2).msg(e.getLocalizedMessage())));
    }

    private void writeException(HttpStatus status, HttpServletRequest request, HttpServletResponse response,String body){
        response.setStatus(status.value());

        String ce = request.getCharacterEncoding();
        response.setCharacterEncoding(ce);
        response.setContentType(MimeTypeUtils.APPLICATION_JSON_VALUE);

        try (OutputStream os = response.getOutputStream()){
            byte[] data = body.getBytes(ce);
            os.write(data, 0, data.length);
            os.flush();
        } catch (IOException e) {
            LogHelper.error(e.getLocalizedMessage(),e );
        }

    }

    private Authentication getAuthentication(String authInfo, HttpServletRequest request) {
        //LogHelper.dev(()->"身份认证token:"+authInfo);
        if (StringUtils.isNotEmpty(authInfo)) {
            String token = authInfo.replaceFirst("Bearer ", "");
            //LogHelper.dev(()->"身份认证-令牌:"+token);
            //获取token中保存的数据
            Claims claims = mmCmpJwtToken.getClaims(authInfo);
            if(claims==null){
                throw new CannotResolveTokenException("无法解析令牌:"+authInfo);
            }

            //对比ip
            String userIp = claims.getId();

            if (!StringUtils.equalsIgnoreCase(userIp,IpAddressUtils.getIpAddress(request))) { //ip异常处理
                throw new IpConflictException("ip变更，请重新登录");
            }

            Date expiration = claims.getExpiration();
            if(System.currentTimeMillis()>=expiration.getTime()){
                TokenCache cache = SpringUtils.getBean(TokenCache.class);
                cache.evictToken(token);
                throw new OutExpirationException("令牌过期:"+authInfo);
            }

            //获取用户名
            String username = claims.getSubject();

            TokenCache cache = SpringUtils.getBean(TokenCache.class);
            UserDetails ud =  cache.getToken(token);


            if(ud==null){
                cache.evictToken(token);
                throw new NotFoundTokenException("无法找到令牌:"+authInfo);
            }

            if(ud instanceof AuthAccount){
                ((AuthAccount) ud).setSessionKey(token);
            }

            if(!StringUtils.equals(ud.getUsername(), username)){
                throw new WrongUserException("");
            }


            Authentication auth = new UsernamePasswordAuthenticationToken(ud.getUsername(),
                    ud, null);
                    //mmServAuth.loadUserAuthentication(ud));

            SecurityContextHolder.getContext().setAuthentication(auth);

            //LogHelper.dev("权限："+auth);

            return auth;
        }


        return null;
    }
}
